package sk.uniza.fri.cerviix.client;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

import sk.uniza.fri.cerviix.client.game.Player;
import sk.uniza.fri.cerviix.client.game.WormPart;
import sk.uniza.fri.cerviix.client.graphics.Font;
import sk.uniza.fri.cerviix.client.graphics.Texture;
import sk.uniza.fri.cerviix.client.network.NetworkManager;
import sk.uniza.fri.cerviix.client.network.ServerMessage;
import sk.uniza.fri.cerviix.client.network.message.client.ConnectMessage;
import sk.uniza.fri.cerviix.client.network.message.client.DisconnectMessage;
import sk.uniza.fri.cerviix.client.network.message.client.WormDirectionMessage;
import sk.uniza.fri.cerviix.client.network.message.client.WormDirectionMessage.Direction;

public class Game
{
	public static final int RESOLUTION_HORIZONTAL = 750;
	public static final int RESOLUTION_VERTICAL = 500;
	
	private static final int KEY_LEFT = Keyboard.KEY_A;
	private static final int KEY_RIGHT = Keyboard.KEY_D;
	
	private NetworkManager networkManager;
	private Font font;
	
	private int playerId = 0;
	private String playerName;
	
	private Map<Integer, Player> players = new HashMap<Integer, Player>();
	private Queue<WormPart> wormsParts = new LinkedList<WormPart>();
	
	private boolean go = true;
	
	private boolean playing = false;
	
	private Player lastWinner = null;
	
	public Game(NetworkManager networkManager, String playerName) throws IOException
	{
		this.networkManager = networkManager;
		this.playerName = playerName;
		
		try
		{
			font = new Font(new Texture("res/DarkFont.png"));
		}
		catch (FileNotFoundException e)
		{
			Client.log.severe("Can't load bitmap font.");
			
			throw new IOException(e);
		}
		
		GL11.glEnable(GL11.GL_TEXTURE_2D);
		GL11.glDisable(GL11.GL_DEPTH_TEST);
	}
	
	public void run() throws IOException
	{
		Client.log.info("Game started.");
		
		networkManager.sendMessage(new ConnectMessage(playerName));
		
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GL11.glOrtho(-50.0f, 100.0f, -50.0f, 50.0f, 1.0f, -1.0f);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		
		while (go)
		{
			if (!networkManager.isConnected())
			{
				throw new IOException();
			}
			
			while (networkManager.hasMessage())
			{
				ServerMessage message = networkManager.getMessage();
				
				message.run(this);
			}
			
			if (playing)
			{
				updateGame();
				drawGame();
			}

			drawPlayersPanel();
			
			Display.update();
			
			if (Display.isCloseRequested())
			{
				go = false;
			}
			
			Display.sync(60);
		}
		
		networkManager.sendMessage(new DisconnectMessage());
	}
	
	public void start()
	{
		wormsParts.clear();
		
		for (Player player : players.values())
		{
			player.reset();
		}
		
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		
		GL11.glBegin(GL11.GL_QUADS);
			GL11.glColor3f(0.0f, 0.0f, 0.0f);
			
			GL11.glVertex2f(-50.0f,-50.0f);
			GL11.glVertex2f( 50.0f,-50.0f);
			GL11.glVertex2f( 50.0f, 50.0f);
			GL11.glVertex2f(-50.0f, 50.0f);
		GL11.glEnd();
		
		setPlaying(true);
	}
	
	public void stop()
	{
		drawGameStopped();
		
		setPlaying(false);
	}
	
	public void updateGame()
	{
		while (Keyboard.next())
		{
			if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE)
			{
				go = false;
				return;
			}
			
			WormDirectionMessage directionMessage = null;
			
			switch (Keyboard.getEventKey())
			{				
				case KEY_LEFT:
					if (Keyboard.getEventKeyState())
					{
						directionMessage = new WormDirectionMessage(Direction.LEFT);
					}
					else
					{
						directionMessage = new WormDirectionMessage(Direction.STRAIGHT);
					}
					
					break;
					
				case KEY_RIGHT:
					if (Keyboard.getEventKeyState())
					{
						directionMessage = new WormDirectionMessage(Direction.RIGHT);
					}
					else
					{
						directionMessage = new WormDirectionMessage(Direction.STRAIGHT);
					}
					
					break;
			}
			
			if (directionMessage != null)
			{
				networkManager.sendMessage(directionMessage);
			}
		}
	}
	
	public void drawGame()
	{
		GL11.glPushMatrix();
		
		GL11.glTranslatef(0.0f, 0.0f, 0.0f);
		
		while (!wormsParts.isEmpty())
		{
			wormsParts.remove().draw();
		}		
		
		GL11.glPopMatrix();
	}
	
	private void drawGameStopped()
	{
		GL11.glPushMatrix();
			GL11.glBegin(GL11.GL_QUADS);
				GL11.glColor3f(0.8f, 0.4f, 0.0f);
				
				GL11.glVertex2f(-45.0f,-10.0f);
				GL11.glVertex2f( 45.0f,-10.0f);
				GL11.glVertex2f( 45.0f, 10.0f);
				GL11.glVertex2f(-45.0f, 10.0f);
			GL11.glEnd();
			
			GL11.glTranslatef(-15.0f, 1.4f, 0.0f);
			font.drawText("match ended");
			
			if (lastWinner != null)
			{
				GL11.glTranslatef(-5.0f, -7.0f, 0.0f);
				font.drawText("winner is " + lastWinner.getName());
			}
			
		GL11.glPopMatrix();
	}
	
	private void drawPlayersPanel()
	{
		GL11.glPushMatrix();
		
			GL11.glTranslatef(50.0f, 0.0f, 0.0f);
			GL11.glBegin(GL11.GL_QUADS);
				GL11.glColor3f(0.3f, 0.3f, 0.3f);
				
				GL11.glVertex2f( 0.0f, 50.0f);
				GL11.glVertex2f( 0.0f,-50.0f);
				GL11.glVertex2f(50.0f,-50.0f);
				GL11.glVertex2f(50.0f, 50.0f);
			GL11.glEnd();
			
			float yStart = 40.0f;
			float offset = -5.0f;
			
			GL11.glPushMatrix();
				GL11.glTranslatef(1.0f, yStart, 0.0f);
				
				for (Player player : players.values())
				{
					GL11.glPushMatrix();
					
					if (player.isAlive())
					{
						GL11.glColor3f(0.0f, 1.0f, 0.0f);
					}
					else
					{
						GL11.glColor3f(1.0f, 0.0f, 0.0f);
					}
					
					GL11.glBegin(GL11.GL_QUADS);
						GL11.glVertex2f( 0.0f, 4.0f);
						GL11.glVertex2f( 4.0f, 4.0f);
						GL11.glVertex2f( 4.0f, 0.0f);
						GL11.glVertex2f( 0.0f, 0.0f);
					GL11.glEnd();
					
					GL11.glTranslatef(5.0f, 0.0f, 0.0f);
					
					GL11.glPushMatrix();
						GL11.glColor3f(0.3f, 0.3f, 0.3f);
						
						font.drawText(Integer.toString(player.getScore()));
						
						GL11.glTranslatef(12.0f, 0.0f, 0.0f);
						
						font.drawText(player.getName());
					GL11.glPopMatrix();
					
					GL11.glPopMatrix();
					
					GL11.glTranslatef(0.0f, offset, 0.0f);
				}
			GL11.glPopMatrix();
			
			GL11.glPushMatrix();
				GL11.glTranslatef(3.0f,-37.0f, 0.0f);
				font.drawText("mbskot");
				
				GL11.glTranslatef(3.0f,-5.0f, 0.0f);
				font.drawText("and");
				
				GL11.glTranslatef(3.0f,-5.0f, 0.0f);
				font.drawText("moergil");
			GL11.glPopMatrix();
		
		GL11.glPopMatrix();
	}
	
	public void setWinner(Player winner)
	{
		lastWinner = winner;
	}
	
	public void setPlaying(boolean playing)
	{		
		this.playing = playing; 
	}
	
	public void setPlayerId(int playerId)
	{
		if (this.playerId != 0)
		{
			throw new RuntimeException("PlayerId already setted!");
		}
		
		Client.log.info(String.format("Player id setted: %d", playerId));
		
		this.playerId = playerId;
	}
	
	public int getPlayerId()
	{
		return playerId;
	}
	
	public String getPlayerName()
	{
		return playerName;
	}
	
	public void addPlayer(Player player)
	{
		if (players.containsKey(player.getId()))
		{
			Client.log.info(String.format("Player with %d is already registered.", player.getId()));
			return;
		}
		
		players.put(player.getId(), player);
		
		Client.log.info(String.format("Player added: %s", player.getName()));
	}
	
	public void removePlayer(int playerId)
	{
		Client.log.info(String.format("Player removed: %s", playerId));
		
		players.remove(playerId);
	}
	
	public Player getPlayer(int id)
	{
		return players.get(id);
	}
	
	public Map<Integer, Player> getPlayers()
	{
		return players;
	}
	
	public void addWormPart(WormPart part)
	{
		wormsParts.add(part);
	}
}
